# Copyright (C) 2006-2008 by Sergio Pistone
# sergio_pistone@yahoo.com.ar
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

require "tk"
require "thread"
require "date"
require File.expand_path( File.dirname( __FILE__ ) + "/../i18n/i18n" )

module TK

	def TK.empty_string?( text )
		return text ? (text.empty? ? true : text.strip().empty?) : true
	end

	def TK.proc_args( args, defined=nil )
		ret = {}
		args.each() { |hash| hash.each() { |k,v| ret[k] = v } }
		defined.each() { |k,v| ret[k] = v } if defined
		return ret
	end

	class Label < TkLabel

		def initialize( parent, text, *args )
			super( parent, TK.proc_args( args, { "text"=> text } ) )
		end

	end

	class Button < TkButton

		def initialize( parent, text, *args )
			super( parent, TK.proc_args( args, { "text"=> text } ) )
		end

		def set_enabled( enabled )
			self.state = enabled ? "normal" : "disabled"
		end

		def is_enabled()
			return state() != "disabled"
		end

	end

	class TextArea < TkText

		def initialize( parent, *args )
			super( parent, TK.proc_args( args ) )
			menu = TkMenu.new( self, "tearoff"=>false )
			# menu.add( "command", "label"=>I18n.get( "gui.common.undo" ), "accel"=>"Ctrl+Z", "command"=>proc{undo()} )
			# menu.add( "command", "label"=>I18n.get( "gui.common.redo" ), "accel"=>"Ctrl+Shift+Z", "command"=>proc{redo2()} )
			# menu.add( "separator" )
			menu.add( "command", "label"=>I18n.get( "gui.common.cut" ), "accel"=>"Ctrl+X", "command"=>proc { cut() } )
			menu.add( "command", "label"=>I18n.get( "gui.common.copy" ), "accel"=>"Ctrl+C", "command"=>proc { copy() } )
			menu.add( "command", "label"=>I18n.get( "gui.common.paste" ), "accel"=>"Ctrl+V", "command"=>proc { paste() } )
			menu.add( "command", "label"=>I18n.get( "gui.common.delete" ), "accel"=>"Delete", "command"=>proc{delete_selection()} )
			menu.add( "separator" )
			menu.add( "command", "label"=>I18n.get( "gui.common.selectall" ), "accel"=>"Ctrl+A", "command"=>proc { select_all() } )
			menu.bind( "FocusOut", proc { menu.unpost() } )

			bind( "FocusOut", proc { select_none() } )
			bind( "Button-3", proc { |e| menu.post( e.x_root, e.y_root ); menu.set_focus() } )
			bind( "Control-Key-X", proc { cut(); Tk.callback_break() } )
			bind( "Control-Key-x", proc { cut(); Tk.callback_break() } )
			bind( "Control-Key-C", proc { copy(); Tk.callback_break() } )
			bind( "Control-Key-c", proc { copy(); Tk.callback_break() } )
			bind( "Control-Key-V", proc { paste(); Tk.callback_break() } )
			bind( "Control-Key-v", proc { paste(); Tk.callback_break() } )
			bind( "Control-Key-A", proc { select_all(); Tk.callback_break() } )
			bind( "Control-Key-a", proc { select_all(); Tk.callback_break() } )
			bind( "Control-Key-Z", proc { undo(); Tk.callback_break() } )
			bind( "Control-Key-z", proc { undo(); Tk.callback_break() } )
			bind( "Control-Shift-Key-Z", proc { redo2(); Tk.callback_break() } )
			bind( "Control-Shift-Key-z", proc { redo2(); Tk.callback_break() } )
		end

		def select_all()
			tag_add( "sel", "0.0", "end" )
		end

		def select_none()
			tag_remove( "sel", "0.0", "end" )
		end

		def copy()
			text_copy()
		end

		def cut()
			text_cut()
		end

		def paste()
			selected = tag_ranges( "sel" ).size > 0
			if selected
				length = value.size
				tag_add( "prev_sel", "sel.first", "sel.last" )
			end
			text_paste()
			delete( "prev_sel.first", "prev_sel.last" ) if selected && value.size != length
			see( "insert" )
		end

		def delete_selection()
 			delete( "sel.first", "sel.last" ) if tag_ranges( "sel" ).size > 0
		end

		def undo()
		end

		def redo2()
		end

		def set_enabled( enabled )
			self.state = enabled ? "normal" : "disabled"
		end

		def is_enabled()
			return state() != "disabled"
		end

		def append_text( text )
			insert( "end", text )
		end

		def get_value()
			value()
		end

		def set_value( value )
			delete( "0.0", "end" )
			insert( "end", value )
		end

		alias value= set_value

		alias get_text get_value
		alias set_text set_value

		alias text get_value
		alias text= set_value

	end

	class SpinBox < TkSpinbox

		def initialize( parent, value, *args )
			@variable = TkVariable.new( value )
			super( parent, TK.proc_args( args, { "textvariable"=> @variable } ) )
		end

		def set_enabled( enabled )
			self.state = enabled ? "normal" : "disabled"
		end

		def is_enabled()
			return state() != "disabled"
		end

		def get_value()
			@variable.value()
		end

		def set_value( value )
			@variable.set_value( value )
		end

		def get_min_value()
			return self.from
		end

		def set_min_value( min_value )
			self.from = min_value
		end

		def get_max_value()
			return self.to
		end

		def set_max_value( max_value )
			self.to = max_value
		end

		def set_range( min_value, max_value )
			set_max_value( max_value )
			set_min_value( min_value )
		end

		alias value get_value
		alias value= set_value

		alias min_value get_min_value
		alias min_value= set_min_value

		alias max_value get_max_value
		alias max_value= set_max_value

	end

	class TextEdit < TkEntry

		def initialize( parent, text, *args )
			@variable = TkVariable.new( text )
			super( parent, TK.proc_args( args, { "textvariable"=> @variable } ) )

			menu = TkMenu.new( self, "tearoff"=>false )
			# menu.add( "command", "label"=>I18n.get( "gui.common.undo" ), "accel"=>"Ctrl+Z", "command"=>proc { undo() } )
			# menu.add( "command", "label"=>I18n.get( "gui.common.redo" ), "accel"=>"Ctrl+Shift+Z", "command"=>proc { redo2() } )
			# menu.add( "separator" )
			menu.add( "command", "label"=>I18n.get( "gui.common.cut" ), "accel"=>"Ctrl+X", "command"=>proc { cut() } )
			menu.add( "command", "label"=>I18n.get( "gui.common.copy" ), "accel"=>"Ctrl+C", "command"=>proc { copy() } )
			menu.add( "command", "label"=>I18n.get( "gui.common.paste" ), "accel"=>"Ctrl+V", "command"=>proc { paste() } )
			menu.add( "command", "label"=>I18n.get( "gui.common.delete" ), "accel"=>"Delete", "command"=>proc{delete_selection()} )
			menu.add( "separator" )
			menu.add( "command", "label"=>I18n.get( "gui.common.selectall" ), "accel"=>"Ctrl+A", "command"=>proc { select_all() } )
			menu.bind( "FocusOut", proc { menu.unpost() } )

			bind( "Button-3", proc { |e| menu.post( e.x_root, e.y_root ); menu.set_focus() } )
			bind( "FocusOut", proc { select_none() } )
			bind( "Control-Key-X", proc { cut(); Tk.callback_break() } )
			bind( "Control-Key-x", proc { cut(); Tk.callback_break() } )
			bind( "Control-Key-C", proc { copy(); Tk.callback_break() } )
			bind( "Control-Key-c", proc { copy(); Tk.callback_break() } )
			bind( "Control-Key-V", proc { paste(); Tk.callback_break() } )
			bind( "Control-Key-v", proc { paste(); Tk.callback_break() } )
			bind( "Control-Key-A", proc { select_all(); Tk.callback_break() } )
			bind( "Control-Key-a", proc { select_all(); Tk.callback_break() } )
			bind( "Control-Key-Z", proc { undo(); Tk.callback_break() } )
			bind( "Control-Key-z", proc { undo(); Tk.callback_break() } )
			bind( "Control-Shift-Key-Z", proc { redo2(); Tk.callback_break() } )
			bind( "Control-Shift-Key-z", proc { redo2(); Tk.callback_break() } )
		end

		def select_all()
			selection_range( 0, "end" )
		end

		def select_none()
			selection_clear()
		end

		def delete_selection()
			delete( "sel.first", "sel.last" ) if selection_present()
		end

		def cut()
			if selection_present()
				TkClipboard.set( value().slice( index( "sel.first" ), index( "sel.last" ) ) )
				delete( "sel.first", "sel.last" )
			end
		end

		def copy()
			TkClipboard.set( value().slice( index( "sel.first" ), index( "sel.last" ) ) ) if selection_present()
		end

		def paste()
			data = TkClipboard.get()
			if ! TK.empty_string?( data )
				insert( index( "insert" ), data )
				xview( "insert" )
				delete( "sel.first", "sel.last" ) if selection_present()
			end
		end

		def undo()
		end

		def redo2()
		end

		def set_enabled( enabled )
			self.state = enabled ? "normal" : "disabled"
		end

		def is_enabled()
			return state() != "disabled"
		end

		def get_value()
			@variable.value()
		end

		def set_value( value )
			@variable.set_value( value )
		end

		alias value get_value
		alias value= set_value

		alias get_text get_value
		alias set_text set_value

		alias text get_value
		alias text= set_value

	end

	class CheckBox < TkCheckButton

		def initialize( parent, text, *args )
			super( parent, TK.proc_args( args, { "text"=> text } ) )
		end

		def set_enabled( enabled )
			self.state = enabled ? "normal" : "disabled"
		end

		def is_enabled()
			return state() != "disabled"
		end

		def get_value()
			super() != "0"
		end

		alias value get_value
		alias value= set_value

		alias is_checked get_value
		alias get_checked get_value
		alias set_checked set_value

		alias checked get_value
		alias checked= set_value

	end

	class BaseDialog

		attr_reader :accepted
		attr_reader :values

		def initialize( values, close_on_escape=true )
			@root = TkRoot.new()
			@root.withdraw()

			@shell = TkToplevel.new( @root )
			@shell.protocol( "WM_DELETE_WINDOW", proc { destroy() } )
			@shell.bind( "Escape", proc { destroy() } ) if close_on_escape
			@shell.withdraw() # hide the window until everything is created

			@values = values
			@x, @y, @width, @height = nil, nil, nil, nil
		end

		def exec()

			@accepted = false

			# center the dialog:
			if ! @x && ! @y
				set_size( @width, @height ) if @width
				width, height = get_size()
				s_width, s_height = BaseDialog.get_screen_size()
				@x, @y = (s_width - width) / 2, (s_height - height) / 2
			end
			set_position( @x, @y )

			@shell.deiconify() # the windows is hidden initially so we have to show it

			Tk.mainloop()
			Tk.restart()
		end

		def destroy()
			@root.destroy()
		end

		def accept()
			@accepted = true
			destroy()
		end

		def set_size( width, height )
			@width, @height = width.to_i(), height.to_i()
			@shell.geometry( "#{@width}x#{@height}" ) if @shell
		end

		def set_position( x, y )
			@x, @y = x.to_i(), y.to_i()
			@shell.geometry( "#{@x > 0 ? "+" : "-"}#{@x}#{@y > 0 ? "+" : "-"}#{@y}" ) if @shell
		end

		def get_size()
			return @width.to_i(), @height.to_i() if @width
			width, height = nil, nil
			if @shell
				md = /([0-9]+)x([0-9]+)[-\+][0-9]+[-\+][0-9]+/.match( @shell.geometry() )
				width, height = md[1].to_i(), md[2].to_i()
			end
			return width, height
		end

		def get_position()
			return @x.to_i(), @y.to_i()
		end

		def BaseDialog.get_screen_size()
			maxsize = Tk.root.maxsize()
			return maxsize[0], maxsize[1]
		end

		def create_action_buttons( mode="right", accept_text=nil, cancel_text=nil )
			parent = self
			buttons_frame = TkFrame.new( @shell )
			accept_text = TK.empty_string?( accept_text ) ? I18n.get( "gui.common.accept" ) : accept_text
			cancel_text = TK.empty_string?( cancel_text ) ? I18n.get( "gui.common.cancel" ) : cancel_text
			@accept_button = Button.new( buttons_frame, accept_text ) { command { parent.accept() } }
			@cancel_button = Button.new( buttons_frame, cancel_text ) { command { parent.destroy() } }
			if mode == "left"
				@accept_button.pack( "side"=>"left" )
				@cancel_button.pack( "side"=>"left" )
			elsif mode == "split"
				@accept_button.pack( "side"=>"left", "fill"=>"x", "expand"=>"true" )
				@cancel_button.pack( "side"=>"right", "fill"=>"x", "expand"=>"true" )
			else
				@cancel_button.pack( "side"=>"right" )
				@accept_button.pack( "side"=>"right" )
			end
			return buttons_frame
		end
		protected :create_action_buttons

	end

	class PluginsManagerDialog < BaseDialog

		def initialize( values )
			super( values )

			set_size( 400, 230 )

			@shell.title( I18n.get( "gui.pluginsmanager.title", @values["script_name"] ) )

			label = Label.new( @shell, I18n.get( "gui.pluginsmanager.sites" ) )
			label.grid( "row"=>1, "column"=>1, "columnspan"=>4, "sticky"=>"w" )

			frame = TkFrame.new( @shell )
			frame.grid( "row"=>2, "rowspan"=>4, "column"=>2, "sticky"=>"nesw" )
			@used_plugins_list = TkListbox.new( frame, "selectmode" => "single" )
			@values["used_plugins"].each() { |plugin| @used_plugins_list.insert( "end", plugin ) }
			@used_plugins_list.pack( "side"=>"left", "fill"=>"both", "expand"=>true )
			y_scroll_bar = TkScrollbar.new( frame, "orient"=>"ver" )
			y_scroll_bar.command( proc { |*args| @used_plugins_list.yview( *args ) } )
			y_scroll_bar.pack( "side"=>"right", "fill"=>"y" )
			@used_plugins_list.yscrollcommand( proc { |first, last| y_scroll_bar.set( first, last ) } )

			frame = TkFrame.new( @shell )
			frame.grid( "row"=>2, "rowspan"=>4, "column"=>4, "sticky"=>"nesw" )
			@unused_plugins_list = TkListbox.new( frame, "selectmode" => "single" )
			@values["unused_plugins"].each() { |plugin| @unused_plugins_list.insert( "end", plugin ) }
			@unused_plugins_list.pack( "side"=>"left", "fill"=>"both", "expand"=>true )
			y_scroll_bar = TkScrollbar.new( frame, "orient"=>"ver" )
			y_scroll_bar.command( proc { |*args| @unused_plugins_list.yview( *args ) } )
			y_scroll_bar.pack( "side"=>"right", "fill"=>"y" )
			@unused_plugins_list.yscrollcommand( proc { |first, last| y_scroll_bar.set( first, last ) } )

			@move_up_button = Button.new( @shell, I18n.get( "gui.pluginsmanager.sites.moveup" ) )
			@move_up_button.command( proc { move_up() } )
			@move_up_button.grid( "row"=>3, "column"=>1, "sticky"=>"ew" )

			@move_down_button = Button.new( @shell, I18n.get( "gui.pluginsmanager.sites.movedown" ) )
			@move_down_button.command( proc { move_down() } )
			@move_down_button.grid( "row"=>4, "column"=>1, "sticky"=>"ew" )

			@add_button = Button.new( @shell, I18n.get( "gui.pluginsmanager.sites.add" ) )
			@add_button.command( proc { add_plugin() } )
			@add_button.grid( "row"=>3, "column"=>3, "sticky"=>"ew" )

			@remove_button = Button.new( @shell, I18n.get( "gui.pluginsmanager.sites.remove" ) )
			@remove_button.command( proc { remove_plugin() } )
			@remove_button.grid( "row"=>4, "column"=>3, "sticky"=>"ew" )

			@cleanup_lyrics_checkbox = CheckBox.new( @shell, I18n.get( "gui.pluginsmanager.misc.cleanup" ) )
			@cleanup_lyrics_checkbox.set_checked( @values["cleanup_lyrics"].to_s() == "true" )
			@cleanup_lyrics_checkbox.grid( "row"=>6, "column"=>1, "columnspan"=>4, "sticky"=>"w" )

			@single_threaded_checkbox = CheckBox.new( @shell, I18n.get( "gui.pluginsmanager.misc.singlethreaded" ) )
			@single_threaded_checkbox.set_checked( @values["single_threaded"].to_s() == "true" )
			@single_threaded_checkbox.grid( "row"=>7, "column"=>1, "columnspan"=>4, "sticky"=>"w" )

			@write_log_checkbox = CheckBox.new( @shell, I18n.get( "gui.pluginsmanager.misc.writelog", "$HOME/.wikilyrics.log" ) )
			@write_log_checkbox.set_checked( @values["write_log"].to_s() == "true" )
			@write_log_checkbox.grid( "row"=>8, "column"=>1, "columnspan"=>4, "sticky"=>"w" )

			buttons = create_action_buttons()
			buttons.grid( "row"=>9, "column"=>1, "columnspan"=>4, "sticky"=>"ew" )

			update_accept_button()

			@shell.grid_rowconfigure( 2, "weight"=>1 )
			@shell.grid_rowconfigure( 5, "weight"=>1 )
			@shell.grid_columnconfigure( 2, "weight"=>1 )
			@shell.grid_columnconfigure( 4, "weight"=>1 )
		end

		def get_active_index( list_box )
			list_box.size.times do |idx|
				return idx if list_box.selection_includes( idx )
			end
			return nil
		end

		def move_up()
			active_idx = get_active_index( @used_plugins_list )
			return if active_idx == nil || active_idx == 0
			active = @used_plugins_list.get( active_idx )
			@used_plugins_list.delete( active_idx )
			@used_plugins_list.insert( active_idx-1, active )
			@used_plugins_list.selection_set( active_idx-1 )
		end

		def move_down()
			active_idx = get_active_index( @used_plugins_list )
			return if active_idx == nil || active_idx == @used_plugins_list.size - 1
			active = @used_plugins_list.get( active_idx )
			@used_plugins_list.delete( active_idx )
			@used_plugins_list.insert( active_idx+1, active )
			@used_plugins_list.selection_set( active_idx+1 )
		end

		def add_plugin()
			active_idx = get_active_index( @unused_plugins_list )
			return if active_idx == nil
			active = @unused_plugins_list.get( active_idx )
			@unused_plugins_list.delete( active_idx )
			@used_plugins_list.insert( "end", active )
			@used_plugins_list.selection_set( "end" )
			update_accept_button()
		end

		def remove_plugin()
			active_idx = get_active_index( @used_plugins_list )
			return if active_idx == nil
			active = @used_plugins_list.get( active_idx )
			@used_plugins_list.delete( active_idx )
			@unused_plugins_list.insert( "end", active )
			@unused_plugins_list.selection_set( "end" )
			update_accept_button()
		end

		def update_accept_button()
			@accept_button.state = @used_plugins_list.size > 0 ? "normal" : "disabled"
		end

		def accept()
			@values = {
				"cleanup_lyrics" => @cleanup_lyrics_checkbox.is_checked(),
				"single_threaded" => @single_threaded_checkbox.is_checked(),
				"write_log" => @write_log_checkbox.is_checked(),
				"script_name" => @values["script_name"],
				"used_plugins" => [],
				"unused_plugins" => []
			}
			@used_plugins_list.size.times do |idx|
				@values["used_plugins"].insert( -1, @used_plugins_list.get( idx ) )
			end
			@unused_plugins_list.size.times do |idx|
				@values["unused_plugins"].insert( -1, @unused_plugins_list.get( idx ) )
			end
			super()
		end

	end


	class WikiPluginDialog < BaseDialog

		def initialize( values )
			super( values )

			set_size( 360, 210 )

			@shell.title( I18n.get( "gui.wikiplugin.title", values["site_name"] ) )

			label = Label.new( @shell, I18n.get( "gui.wikiplugin.general" ) )
			label.grid( "row"=>1, "column"=>1, "columnspan"=>4, "sticky"=>"w" )

			@submit_checkbox = CheckBox.new( @shell, I18n.get( "gui.wikiplugin.general.submit", values["site_name"] ) )
			@submit_checkbox.set_checked( values["submit"].to_s() == "true" )
			@submit_checkbox.command( proc { toggle_submit_checked() } )
			@submit_checkbox.grid( "row"=>2, "column"=>1, "columnspan"=>3, "sticky"=>"w" )

			@review_checkbox = CheckBox.new( @shell, I18n.get( "gui.wikiplugin.general.review" ) )
			@review_checkbox.command( proc { toggle_review_checked() } )
			@review_checkbox.set_enabled( @submit_checkbox.is_checked() )
			@review_checkbox.set_checked( @review_checkbox.is_enabled() && values["review"].to_s() == "true" )
			@review_checkbox.grid( "row"=>3, "column"=>1, "columnspan"=>3, "sticky"=>"w" )

			@prompt_autogen_checkbox = CheckBox.new( @shell, I18n.get( "gui.wikiplugin.general.autogen" ) )
			@prompt_autogen_checkbox.set_enabled( @review_checkbox.is_checked() )
			@prompt_autogen_checkbox.set_checked( @prompt_autogen_checkbox.is_enabled() && values["prompt_autogen"].to_s() == "true" )
			@prompt_autogen_checkbox.grid( "row"=>4, "column"=>1, "columnspan"=>3, "sticky"=>"w" )

			@prompt_no_lyrics_checkbox = CheckBox.new( @shell, I18n.get( "gui.wikiplugin.general.nolyrics" ) )
			@prompt_no_lyrics_checkbox.set_enabled( @review_checkbox.is_checked() )
			@prompt_no_lyrics_checkbox.set_checked( @prompt_no_lyrics_checkbox.is_enabled() && values["prompt_no_lyrics"].to_s() == "true" )
			@prompt_no_lyrics_checkbox.grid( "row"=>5, "column"=>1, "columnspan"=>3, "sticky"=>"w" )

			label = Label.new( @shell, I18n.get( "gui.wikiplugin.login" ) )
			label.grid( "row"=>7, "column"=>1, "columnspan"=>4, "sticky"=>"w" )

			label = Label.new( @shell, I18n.get( "gui.wikiplugin.login.username" ) )
			label.grid( "row"=>8, "column"=>1 )
			@username_lineedit = TextEdit.new( @shell, values["username"] )
			@username_lineedit.grid( "row"=>8, "column"=>2, "columnspan"=>2, "sticky"=>"ew" )

			label = Label.new( @shell, I18n.get( "gui.wikiplugin.login.password" ) )
			label.grid( "row"=>9, "column"=>1 )
			@password_lineedit = TextEdit.new( @shell, values["password"], "show"=>"*" )
			@password_lineedit.grid( "row"=>9, "column"=>2, "columnspan"=>2, "sticky"=>"ew" )

			@username_lineedit.bind( "KeyRelease", proc { update_accept_button_state() } )

			buttons = create_action_buttons()
			buttons.grid( "row"=>11, "column"=>1, "columnspan"=>4, "sticky"=>"ew" )
			update_accept_button_state()

			@shell.grid_rowconfigure( 6, "weight"=>1 )
			@shell.grid_rowconfigure( 10, "weight"=>5 )
			@shell.grid_columnconfigure( 2, "weight"=>1 )
			@shell.grid_columnconfigure( 3, "weight"=>1 )
		end

		def toggle_submit_checked()
			@review_checkbox.set_enabled( @submit_checkbox.is_checked() )
			if ! @review_checkbox.is_enabled()
				@review_checkbox.set_checked( false )
				@prompt_autogen_checkbox.set_checked( false )
				@prompt_no_lyrics_checkbox.set_checked( false )
				@prompt_autogen_checkbox.set_enabled( false )
				@prompt_no_lyrics_checkbox.set_enabled( false )
			end
			update_accept_button_state()
		end

		def toggle_review_checked()
			enabled = @review_checkbox.is_checked()
			@prompt_autogen_checkbox.set_enabled( enabled )
			@prompt_no_lyrics_checkbox.set_enabled( enabled )
			if ! enabled
				@prompt_autogen_checkbox.set_checked( false )
				@prompt_no_lyrics_checkbox.set_checked( false )
			end
		end

		def update_accept_button_state()
			@accept_button.set_enabled( ! @submit_checkbox.is_checked() || ! @username_lineedit.text().empty? )
		end

		def accept()
			@values = {
				"submit"			=> @submit_checkbox.is_checked(),
				"review"			=> @review_checkbox.is_checked(),
				"prompt_autogen"	=> @prompt_autogen_checkbox.is_checked(),
				"prompt_no_lyrics"	=> @prompt_no_lyrics_checkbox.is_checked(),
				"username"			=> @username_lineedit.text(),
				"password"			=> @password_lineedit.text(),
			}
			super()
		end

	end

	class SubmitSongDialog < BaseDialog

		def initialize( values )
			super( values )

			set_size( 600, 400 )

			edit_mode = @values["edit_mode"].to_s() == "true"
			@shell.title( I18n.get( "gui.submitsong.title." + (edit_mode ? "edit" : "submit"), values["site_name"] ) )

			artist_label = Label.new( @shell, I18n.get( "gui.common.artist" ) )
			artist_label.grid( "row"=>1, "column"=>1, "sticky"=>"ew" )
			@artist_lineedit = TextEdit.new( @shell, values["artist"] )
			@artist_lineedit.grid( "row"=>1, "column"=>2, "sticky"=>"ew" )

			title_label = Label.new( @shell, I18n.get( "gui.common.song" ) )
			title_label.grid( "row"=>1, "column"=>3, "sticky"=>"ew" )
			@title_lineedit = TextEdit.new( @shell, values["title"] )
			@title_lineedit.grid( "row"=>1, "column"=>4, "sticky"=>"ew" )

			credits_label = Label.new( @shell, I18n.get( "gui.common.credits" ) )
			credits_label.grid( "row"=>2, "column"=>1, "sticky"=>"ew" )
			@credits_lineedit = TextEdit.new( @shell, values["credits"] )
			@credits_lineedit.grid( "row"=>2, "column"=>2, "sticky"=>"ew" )

			lyricist_label = Label.new( @shell, I18n.get( "gui.common.lyricist" ) )
			lyricist_label.grid( "row"=>2, "column"=>3, "sticky"=>"ew" )
			@lyricist_lineedit = TextEdit.new( @shell, values["lyricist"] )
			@lyricist_lineedit.grid( "row"=>2, "column"=>4, "sticky"=>"ew" )

			year_label = Label.new( @shell, I18n.get( "gui.common.year" ) )
			year_label.grid( "row"=>3, "column"=>1, "sticky"=>"ew" )
			@year_spinbox = SpinBox.new( @shell, values["year"] )
			@year_spinbox.set_range( 1900, Date.today().year )
			@year_spinbox.grid( "row"=>3, "column"=>2, "sticky"=>"ew" )

			album_label = Label.new( @shell, I18n.get( "gui.common.album" ) )
			album_label.grid( "row"=>3, "column"=>3, "sticky"=>"ew" )
			@album_lineedit = TextEdit.new( @shell, values["album"] )
			@album_lineedit.grid( "row"=>3, "column"=>4, "sticky"=>"ew" )

			@instrumental_checkbox = CheckBox.new( @shell, I18n.get( "gui.submitsong.instrumental" ) )
			@instrumental_checkbox.set_checked( values["instrumental"].to_s() == "true" )
			@instrumental_checkbox.command = proc { toggle_instrumental_checked() }
			@instrumental_checkbox.grid( "row"=>4, "column"=>1, "columnspan"=>3, "sticky"=>"w" )

			lyrics_frame = TkFrame.new( @shell )
			lyrics_frame.grid( "row"=>5, "column"=>1, "columnspan"=>4, "sticky"=>"nesw" )
			bar = TkScrollbar.new( lyrics_frame, "orient"=>"ver" )
			bar.pack( "side"=>"right", "fill"=>"y" )
			@lyrics_text = TextArea.new( lyrics_frame ) { yscrollcommand { |first, last| bar.set( first, last ) } }
			@lyrics_text.set_text( values["lyrics"] )
			@lyrics_text.pack( "side"=>"left", "fill"=>"both", "expand"=>true )
			bar.command( proc { |*args| @lyrics_text.yview( *args ) } )
 			toggle_instrumental_checked()

			@reviewed_checkbox = CheckBox.new( @shell, I18n.get( "gui.common.reviewed" ) )
			@reviewed_checkbox.set_checked( false )
			@reviewed_checkbox.grid( "row"=>6, "column"=>1, "columnspan"=>4, "sticky"=>"w" )

			buttons = create_action_buttons( "split", I18n.get( "gui.common.submit" ) )
			buttons.grid( "row"=>7, "column"=>1, "columnspan"=>4, "sticky"=>"ew" )

			@shell.grid_rowconfigure( 5, "weight"=>1 )
			4.times { |idx| @shell.grid_columnconfigure( idx+1, "weight"=>1 ) }
		end

		def toggle_instrumental_checked()
			@lyrics_text.set_enabled( ! @instrumental_checkbox.is_checked() )
		end

		def accept()
			@values = {
				"artist"		=> @artist_lineedit.text(),
				"year"			=> @year_spinbox.value(),
				"album"			=> @album_lineedit.text(),
				"title"			=> @title_lineedit.text(),
				"lyrics"		=> @lyrics_text.text(),
				"instrumental"	=> @instrumental_checkbox.is_checked(),
				"lyricist"		=> @lyricist_lineedit.text(),
				"credits"		=> @credits_lineedit.text(),
				"reviewed"		=> @reviewed_checkbox.is_checked()
			}
			super()
		end

	end


	class SubmitAlbumDialog < BaseDialog

		def initialize( values )
			super( values )

			set_size( 600, 400 )

			@shell.title( I18n.get( "gui.submitalbum.title", values["site_name"] ) )

			artist_label = Label.new( @shell, I18n.get( "gui.common.artist" ) )
			artist_label.grid( "row"=>1, "column"=>1, "sticky"=>"ew" )
			@artist_lineedit = TextEdit.new( @shell, values["artist"] )
			@artist_lineedit.grid( "row"=>1, "column"=>2, "columnspan"=>5, "sticky"=>"ew" )

			album_label = Label.new( @shell, I18n.get( "gui.common.album" ) )
			album_label.grid( "row"=>2, "column"=>1, "sticky"=>"ew" )
			@album_lineedit = TextEdit.new( @shell, values["album"] )
			@album_lineedit.grid( "row"=>2, "column"=>2, "columnspan"=>5, "sticky"=>"ew" )

			label = Label.new( @shell, I18n.get( "gui.common.year" ) )
			label.grid( "row"=>3, "column"=>1, "sticky"=>"ew" )
			@year_spinbox = SpinBox.new( @shell, values["year"].to_i() )
			@year_spinbox.set_range( 1900, Date.today().year )
			@year_spinbox.grid( "row"=>3, "column"=>2, "sticky"=>"ew" )

			label = Label.new( @shell, I18n.get( "gui.common.month" ) )
			label.grid( "row"=>3, "column"=>3, "sticky"=>"ew" )
			@month_spinbox = SpinBox.new( @shell, values["month"].to_i() )
			@month_spinbox.set_range( 0, 12 )
			@month_spinbox.grid( "row"=>3, "column"=>4, "sticky"=>"ew" )

			label = Label.new( @shell, I18n.get( "gui.common.day" ) )
			label.grid( "row"=>3, "column"=>5, "sticky"=>"ew" )
			@day_spinbox = SpinBox.new( @shell, values["day"].to_i() )
			@day_spinbox.set_range( 0, 31 )
			@day_spinbox.grid( "row"=>3, "column"=>6, "sticky"=>"ew" )

			image_label = Label.new( @shell, I18n.get( "gui.common.image" ) )
			image_label.grid( "row"=>4, "column"=>1, "sticky"=>"ew" )

			image_frame = TkFrame.new( @shell )
			image_frame.grid( "row"=>4, "column"=>2, "columnspan"=>5, "sticky"=>"ew" )

			@image_path_lineedit = TextEdit.new( image_frame, @values.include?( "image_path" ) ? @values["image_path"] : I18n.get( "gui.submitalbum.coverfound" ) )
			@image_path_lineedit.set_enabled( @values.include?( "image_path" ) )
			@image_path_lineedit.pack( "side"=>"left", "fill"=>"both", "expand"=>true )

			if values.include?( "image_path" )
				@image_button = Button.new( image_frame, "..." )
				@image_button.command( proc { browse_image() } )
				@image_button.pack( "side"=>"right" )
			end

			tracks_frame = TkFrame.new( @shell )
			tracks_frame.grid( "row"=>5, "column"=>1, "columnspan"=>6, "sticky"=>"nesw" )
			bar = TkScrollbar.new( tracks_frame, "orient"=>"ver" )
			bar.pack( "side"=>"right", "fill"=>"y" )
			@tracks_text = TextArea.new( tracks_frame ) { yscrollcommand { |first, last| bar.set( first, last ) } }
			@tracks_text.set_text( values["tracks"] )
			@tracks_text.pack( "side"=>"left", "fill"=>"both", "expand"=>true )
			bar.command( proc { |*args| @tracks_text.yview( *args ) } )

			@reviewed_checkbox = CheckBox.new( @shell, I18n.get( "gui.common.reviewed" ) )
			@reviewed_checkbox.set_checked( false )
			@reviewed_checkbox.grid( "row"=>6, "column"=>1, "columnspan"=>6, "sticky"=>"w" );

			buttons = create_action_buttons( "split", I18n.get( "gui.common.submit" ) )
			buttons.grid( "row"=>7, "column"=>1, "columnspan"=>6, "sticky"=>"ew" )

			@shell.grid_rowconfigure( 5, "weight"=>1 )
			6.times { |idx| @shell.grid_columnconfigure( idx+1, "weight"=>1 ) }
		end

		def browse_image()
			dirname = @image_path_lineedit.text().strip()
			dirname = TK.empty_string?( dirname ) ? (ENV["HOME"] ? ENV["HOME"] : ".") : File.dirname( dirname )
			image_path = Tk.getOpenFile( {
				"parent" => @shell,
				"title" => I18n.get( "gui.uploadcover.browseimage.title" ),
				"initialdir" => dirname,
				"filetypes" =>  [
					[I18n.get( "gui.uploadcover.browseimage.images" ), ".jpg .JPG .png .PNG .bmp .BMP"],
					[I18n.get( "gui.uploadcover.browseimage.allfiles" ), "*"]
				]
			} )
			@image_path_lineedit.set_value( image_path.strip() ) if ! TK.empty_string?( image_path )
		end

		def accept()
			aux = {
				"artist"	=> @artist_lineedit.text(),
				"album"		=> @album_lineedit.text(),
				"year"		=> @year_spinbox.value(),
				"month"		=> @month_spinbox.value(),
				"day"		=> @day_spinbox.value(),
				"tracks"	=> @tracks_text.text(),
				"reviewed"	=> @reviewed_checkbox.is_checked()
			}
			aux["image_path"] = @image_path_lineedit.text() if @values.include?( "image_path" )
			@values = aux
			super()
		end

	end


	class UploadCoverDialog < BaseDialog

		def initialize( values )
			super( values )

			set_size( 380, 180 )

			@shell.title( I18n.get( "gui.uploadcover.title", values["site_name"] ) )

			label = Label.new( @shell, I18n.get( "gui.common.album" ) )
			label.grid( "row"=>1, "column"=>1, "sticky"=>"w" )

			label = Label.new( @shell, I18n.get( "gui.common.artist" ) )
			label.grid( "row"=>2, "column"=>1 )
			@artist_lineedit = TextEdit.new( @shell, @values["artist"] )
			@artist_lineedit.grid( "row"=>2, "column"=>2, "sticky"=>"ew" )

			label = Label.new( @shell, I18n.get( "gui.common.album" ) )
			label.grid( "row"=>3, "column"=>1 )
			@album_lineedit = TextEdit.new( @shell, @values["album"] )
			@album_lineedit.grid( "row"=>3, "column"=>2, "sticky"=>"ew" )

			label = Label.new( @shell, I18n.get( "gui.common.year" ) )
			label.grid( "row"=>4, "column"=>1 )
			@year_spinbox = SpinBox.new( @shell, values["year"] )
			@year_spinbox.set_range( 1900, Date.today().year )
			@year_spinbox.grid( "row"=>4, "column"=>2, "sticky"=>"ew" )

			label = Label.new( @shell, I18n.get( "gui.common.image" ) )
			label.grid( "row"=>6, "column"=>1, "columnspan"=>2, "sticky"=>"w" )

			label = Label.new( @shell, I18n.get( "gui.uploadcover.imagepath" ) )
			label.grid( "row"=>7, "column"=>1, "sticky"=>"ew" )
			image_frame = TkFrame.new( @shell )
			image_frame.grid( "row"=>7, "column"=>2, "sticky"=>"ew" )
			@image_path_lineedit = TextEdit.new( image_frame, @values["image_path"] )
			@image_path_lineedit.pack( "side"=>"left", "fill"=>"both", "expand"=>true )

			@image_button = Button.new( image_frame, "..." )
			@image_button.command( proc { browse_image() } )
			@image_button.pack( "side"=>"right" )

			buttons = create_action_buttons()
			buttons.grid( "row"=>9, "column"=>1, "columnspan"=>4, "sticky"=>"ew" )

			@shell.grid_rowconfigure( 5, "weight"=>1 )
			@shell.grid_rowconfigure( 8, "weight"=>1 )
			@shell.grid_columnconfigure( 2, "weight"=>1 )
		end

		def browse_image()
			dirname = @image_path_lineedit.text().strip()
			dirname = TK.empty_string?( dirname ) ? (ENV["HOME"] ? ENV["HOME"] : ".") : File.dirname( dirname )
			image_path = Tk.getOpenFile( {
				"parent" => @shell,
				"title" => I18n.get( "gui.uploadcover.browseimage.title" ),
				"initialdir" => dirname,
				"filetypes" =>  [
					[I18n.get( "gui.uploadcover.browseimage.images" ), ".jpg .JPG .png .PNG .bmp .BMP"],
					[I18n.get( "gui.uploadcover.browseimage.allfiles" ), "*"]
				]
			} )
			@image_path_lineedit.set_text( image_path.strip() ) if ! TK.empty_string?( image_path )
		end

		def accept()
			@values = {
				"artist"		=> @artist_lineedit.text(),
				"album"			=> @album_lineedit.text(),
				"year"			=> @year_spinbox.value(),
				"image_path"	=> @image_path_lineedit.text()
			}
			super()
		end

	end


	class FixPagesDialog < BaseDialog

		def initialize( values )
			super( values )

			@fetch_func = eval( @values["fetch_func"] )
			@fix_func = eval( @values["fix_func"] )
			@submit_func = eval( @values["submit_func"] )

			set_size( 600, 400 )

			@shell.title( "#{values["site_name"]} - #{values["title"]}" )

			label = Label.new( @shell, I18n.get( "gui.common.url" ) )
			label.grid( "row"=>1, "column"=>1 )
			@url_lineedit = TextEdit.new( @shell, values["url"] )
			@url_lineedit.grid( "row"=>1, "column"=>2, "columnspan"=>3, "sticky"=>"ew" )

			page_frame = TkFrame.new( @shell )
			page_frame.grid( "row"=>2, "column"=>1, "columnspan"=>4, "sticky"=>"nesw" )
			bar = TkScrollbar.new( page_frame, "orient"=>"ver" )
			bar.pack( "side"=>"right", "fill"=>"y" )
			@page_text = TextArea.new( page_frame ) { yscrollcommand { |first, last| bar.set( first, last ) } }
			@page_text.pack( "side"=>"left", "fill"=>"both", "expand"=>true )
			bar.command( proc { |*args| @page_text.yview( *args ) } )

			buttons_frame = TkFrame.new( @shell )
			@submit_button = Button.new( buttons_frame, I18n.get( "gui.common.submit" ) )
			@submit_button.command( proc { submit_page() } )
			@submit_button.pack( "side"=>"right" )
			@fix_button = Button.new( buttons_frame, I18n.get( "gui.common.fix" ) )
			@fix_button.command( proc { fix_page() }  )
			@fix_button.pack( "side"=>"right" )
			@fetch_button = Button.new( buttons_frame, I18n.get( "gui.common.load" ) )
			@fetch_button.command( proc { fetch_page() } )
			@fetch_button.pack( "side"=>"right" )
			buttons_frame.grid( "row"=>3, "column"=>1, "columnspan"=>4, "sticky"=>"ew" )

			@shell.grid_rowconfigure( 2, "weight"=>1 )
			@shell.grid_columnconfigure( 2, "weight"=>1 )
		end

		def fetch_page()
			if (page_content = @fetch_func.call( @url_lineedit.value() ))
				@original_content = page_content
				@original_url = @url_lineedit.text()
				@page_text.set_text( page_content )
			end
		end

		def fix_page()
			@page_text.set_text( @fix_func.call( @page_text.text() ) ) if @original_content
		end

		def submit_page()
			if @original_content != nil && @original_content != (page_content = @page_text.text())
				if @submit_func.call( @original_url, page_content )
					@original_content = page_content
					puts I18n.get( "gui.fixpages.success", @original_url )
				else
					puts I18n.get( "gui.fixpages.error", @original_url )
				end
			end
		end

	end


	class SearchLyricsDialog < BaseDialog

		def initialize( values )
			super( values )

			set_size( 300, 140 )

			@shell.title( I18n.get( "gui.searchlyrics.title" ) )

			label = Label.new( @shell, I18n.get( "gui.searchlyrics.search" ) )
			label.grid( "row"=>1, "column"=>1, "columnspan"=>2, "sticky"=>"w" )

			label = Label.new( @shell, I18n.get( "gui.common.artist" ) )
			label.grid( "row"=>2, "column"=>1 )

			@artist_lineedit = TextEdit.new( @shell, @values["artist"] )
			@artist_lineedit.grid( "row"=>2, "column"=>2, "columnspan"=>2, "sticky"=>"ew" )

			label = Label.new( @shell, I18n.get( "gui.common.song" ) )
			label.grid( "row"=>3, "column"=>1 )
			@title_lineedit = TextEdit.new( @shell, @values["title"] )
			@title_lineedit.grid( "row"=>3, "column"=>2, "columnspan"=>2, "sticky"=>"ew" )

			label = Label.new( @shell, I18n.get( "gui.common.album" ) )
			label.grid( "row"=>4, "column"=>1 )
			@album_lineedit = TextEdit.new( @shell, @values["album"] )
			@album_lineedit.grid( "row"=>4, "column"=>2, "columnspan"=>2, "sticky"=>"ew" )

			label = Label.new( @shell, I18n.get( "gui.common.year" ) )
			label.grid( "row"=>5, "column"=>1 )
			@year_spinbox = SpinBox.new( @shell, values["year"] )
			@year_spinbox.set_range( 1900, Date.today().year )
			@year_spinbox.grid( "row"=>5, "column"=>2, "columnspan"=>2, "sticky"=>"ew" )

			buttons = create_action_buttons()
			buttons.grid( "row"=>7, "column"=>1, "columnspan"=>4, "sticky"=>"ew" )

			@shell.grid_rowconfigure( 6, "weight"=>1 )
			@shell.grid_columnconfigure( 2, "weight"=>1 )
			@shell.grid_columnconfigure( 3, "weight"=>1 )
		end

		def accept()
			@values = {
				"artist"	=> @artist_lineedit.text(),
				"title"		=> @title_lineedit.text(),
				"album"		=> @album_lineedit.text(),
				"year"		=> @year_spinbox.value(),
			}
			super()
		end

	end


	class ShowLyricsDialog < BaseDialog

		def initialize( values )
			super( values )

			set_size( 350, 400 )

			title = I18n.get( "gui.lyrics.title", @values["title"], @values["artist"] )
			title += " [#{@values["site_name"]}]" if @values["site_name"]
			@shell.title( title )

			lyrics_frame = TkFrame.new( @shell )
			lyrics_frame.grid( "row"=>1, "column"=>1, "sticky"=>"nesw" )
			bar = TkScrollbar.new( lyrics_frame, "orient"=>"ver" )
			bar.pack( "side"=>"right", "fill"=>"y" )
			@lyrics_text = TextArea.new( lyrics_frame ) { yscrollcommand { |first, last| bar.set( first, last ) } }
			@lyrics_text.set_text( @values["lyrics"] )
			@lyrics_text.pack( "side"=>"left", "fill"=>"both", "expand"=>true )
			bar.command( proc { |*args| @lyrics_text.yview( *args ) } )

			@shell.grid_rowconfigure( 1, "weight"=>1 )
			@shell.grid_columnconfigure( 1, "weight"=>1 )
		end

	end

end
